#include "ogrebindingPCH.h"
#include "scenebinding.h"
#include "entitybinding.h"
#include "camerabinding.h"
#include "lightbinding.h"

namespace lua_ogre {

const char *SceneClassName = "Scene";

LuaScene::LuaScene(Ogre::SceneManager *scn) : c_obj(scn)
{}

LuaScene::~LuaScene()
{
	if (c_obj != NULL)
	{
		c_obj->removeListener(this);
		Ogre::Root::getSingleton().destroySceneManager(c_obj);
	}
}

void LuaScene::sceneManagerDestroyed(Ogre::SceneManager *scn)
{
	if (NULL != c_obj)
	{
		c_obj->removeListener(this);
		c_obj = NULL;
	}
}

const char* LuaScene::GetName() const
{
	if (NULL == c_obj)
	{
		return "Invalid Scene";
	}
	return c_obj->getName().c_str();
}

static int CreateCamera(Ogre::SceneManager *scn, lua_State *ls)
{
	int arg_count = lua_gettop(ls);
	if (arg_count != 1
		|| !lua_isstring(ls, 1))
	{
		LogLuaError << "Scene:CreateCamera failed, name must specify!";
		return 0;
	}
	
	Ogre::Camera *cam = NULL;
	const char *name = lua_tostring(ls, 1);
	if (NULL == name || '\0' == name[0])
	{
		LogLuaError << "Scene::CreateCamera failed, invalid name!";
		return 0;
	}
	else
	{
		cam = scn->createCamera(name);
	}

	if (NULL == cam)
	{
		LogLuaError << "create camera failed!";
		return 0;
	}

	cam->setAutoAspectRatio(true);
	return PushLuaCamera(cam, ls);
}

static int DestroyCamera(Ogre::SceneManager *scn, lua_State *ls)
{
	if (lua_gettop(ls) != 1 
		|| !luaL_checkudata(ls, 1, CameraClassName))
	{
		LogLuaError << "Scene:DestroyCamera(camera) failed, invalid parameter!";
		return 0;
	}

	LuaCamera *lua_cam = (LuaCamera*)lua_touserdata(ls, 1);
	if (lua_cam->c_obj)
	{
		lua_cam->c_obj->removeListener(lua_cam);
		scn->destroyCamera(lua_cam->c_obj);
		lua_cam->c_obj = 0;
	}
	return 0;
}

static int CreateEntity(Ogre::SceneManager *scn, lua_State *ls)
{
	int arg_count = lua_gettop(ls);
	if (arg_count == 0)
	{
		LogLuaError << "Scene:CreateEntity() failed! invalid parameter!";
		return 0;
	}

	const char *entity_name = NULL;
	if (arg_count > 2 && lua_isstring(ls, 2))
	{
		entity_name = lua_tostring(ls, 2);
	}

	if (NULL != entity_name && scn->hasEntity(entity_name))
	{
		LogLuaError << "Scene:CreateEntity() failed! entity named is already exist!" << entity_name;
		return 0;
	}

	Ogre::Entity *entity = NULL;
	if (lua_isnumber(ls, 1))
	{
		Ogre::SceneManager::PrefabType type = (Ogre::SceneManager::PrefabType)lua_tointeger(ls, 1);
		if (NULL == entity_name)
		{
			entity = scn->createEntity(type);
		}
		else
		{
			entity = scn->createEntity(entity_name, type);
		}
	}
	else if (lua_isstring(ls, 1))
	{
		if (NULL != entity_name)
		{
			entity = scn->createEntity(entity_name, lua_tostring(ls, 1));
		}
		else
		{
			entity = scn->createEntity(lua_tostring(ls, 1));
		}
	}
	
	if (NULL == entity)
	{
		LogLuaError << "Scene:CreateEntity() failed, invalid parameters!";
		return 0;
	}

	scn->getRootSceneNode()->createChildSceneNode()->attachObject(entity);

	return LuaCreateEntity(entity, ls);
}

static int DestroyEntity(Ogre::SceneManager *self, lua_State *ls)
{
	if (1 != lua_gettop(ls) 
		|| !luaL_checkudata(ls, 1, EntityClassName))
	{
		LogLuaError << "Scene:DestroyEntity(entity) failed, invalid parameter!";
		return 0;
	}

	LuaEntity *lua_entity = (LuaEntity*)lua_touserdata(ls, 1);
	if (lua_entity->c_obj)
	{
		lua_entity->c_obj->setListener(0);
		self->destroyEntity(lua_entity->c_obj);
		lua_entity->c_obj = 0;
	}
	return 0;
}

static int CreateLight(Ogre::SceneManager *scn, lua_State *ls)
{
	Ogre::Light::LightTypes type = Ogre::Light::LT_POINT;
	Ogre::Light *light = scn->createLight();
	if (lua_gettop(ls) == 1 && lua_isnumber(ls, 1))
	{
		type = (Ogre::Light::LightTypes)lua_tointeger(ls, 1);
	}
	scn->getRootSceneNode()->createChildSceneNode()->attachObject(light);
	light->setType(type);
	return LuaCreateLight(light, ls);
}

static int DestroyLight(Ogre::SceneManager *scn, lua_State *ls)
{
	if (1 != lua_gettop(ls) 
		|| !luaL_checkudata(ls, 1, LightClassName))
	{
		LogLuaError << "Scene:DestroyLight(light) failed!";
		return 0;
	}

	LuaLight *lua_light = (LuaLight*)lua_touserdata(ls, 1);
	if (lua_light->c_obj)
	{
		lua_light->c_obj->setListener(0);
		scn->destroyLight(lua_light->c_obj);
		lua_light->c_obj = 0;
	}
	return 0;
}

static int GetAmbient(Ogre::SceneManager *self, lua_State *ls)
{
	char value[20];
	sprintf(value, "0x%x", self->getAmbientLight().getAsARGB());
	lua_pushlstring(ls, value, strlen(value));
	return 1;
}

static int SetAmbient(Ogre::SceneManager *self, lua_State *ls)
{
	if (lua_gettop(ls) != 1 || !lua_isnumber(ls, 1))
	{
		LogLuaError << "set Scene.ambient error, invalid value!";
		return 0;
	}

	Ogre::ColourValue color;
	color.setAsARGB(Ogre::uint32(lua_tonumber(ls, 1)));
	self->setAmbientLight(color);
	return 0;
}

static LuaMethod<Ogre::SceneManager> g_scene_methods_and_props[] = 
{
	{"CreateEntity", CreateEntity, 0, 0},
	{"DestroyEntity", DestroyEntity, 0, 0},
	{"CreateCamera", CreateCamera, 0, 0},
	{"DestroyCamera", DestroyCamera, 0, 0},
	{"CreateLight", CreateLight, 0, 0},
	{"DestroyLight", DestroyLight, 0, 0},
	{"ambient", 0, GetAmbient, SetAmbient},
	{0, 0}
};

static int SceneAlloc(lua_State *ls)
{
	lua_remove(ls, 1);

	int arg_count = lua_gettop(ls);
	const char *name = "UntitledScene";

	if (arg_count == 1 && lua_isstring(ls, 1))
	{
		name = lua_tostring(ls, 1);
	}

	Ogre::SceneManager *scn = Ogre::Root::getSingleton().createSceneManager(
			Ogre::DefaultSceneManagerFactory::FACTORY_TYPE_NAME, name);

	if (NULL == scn)
	{
		return 0;
	}

	void *mem = lua_newuserdata(ls, sizeof(LuaScene));
	if (NULL == mem)
	{
		return 0;
	}
	new(mem) LuaScene(scn);
	luaL_getmetatable(ls, SceneClassName);
	lua_setmetatable(ls, -2);
	
	return 1;
}

void BindScene(lua_State *ls)
{
	typedef LuaClassBind<Ogre::SceneManager, LuaScene> SceneBind;

	LuaTypeObject<Ogre::SceneManager> type_obj(
		SceneClassName,
		g_scene_methods_and_props, 
		SceneAlloc,
		SceneBind::Dealloc,
		SceneBind::ToString,
		SceneBind::Thunk);

	SceneBind::Register(ls, "ov", type_obj);
}

}